#include "mfGraphics.h"

//----------------------------------------------------------
// static
static float	drawMode			= OF_FILLED;
float			bgColor[4]			= {0,0,0,0};
bool			bBackgroundAuto		= true;
float			basicShapeCoords[8];
bool			bSetupCircle		= false;
int				circleResolution	= 22;
float			circleCoords[MAX_CIRC_RESOLUTION*2+4];
int				vertexCount			= 0;
float			vertexCoords[40000]; // enough to draw 20,000 vertices, using 160kb of mem.


//----------------------------------------------------------
void ofSetupScreen()
{
	// ortho view
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrthof(0, width, -height, 0, -1.0f, 1.0f);
	glScalef(1, -1, 1);
	if( mfGetOrientation() == MF_LANDSCAPE ){
		glRotatef(90, 0,0,1);
		glTranslatef(0, -320, 0);
	}
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	ofClearBackground();
}

void ofClearBackground()
{
	// clear background if we're doing it automatically
	if (ofbClearBg()){
		float *bgColor = ofBgColorPtr();
		glClearColor(bgColor[0],bgColor[1],bgColor[2], bgColor[3]);
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}
}

//----------------------------------------------------------
void ofSetBackgroundAuto(bool bAuto){
	bBackgroundAuto = bAuto;
}

//----------------------------------------------------------
bool ofbClearBg(){
	return bBackgroundAuto;
}

//----------------------------------------------------------
float * ofBgColorPtr(){
	return bgColor;
}

//----------------------------------------------------------
void ofBackground(int r, int g, int b)
{	
	bgColor[0] = (float)r / (float)255.0f;
	bgColor[1] = (float)g / (float)255.0f;
	bgColor[2] = (float)b / (float)255.0f;
	bgColor[3] = 1.0f;
	// if we are in not-auto mode, then clear with a bg call...
	if (ofbClearBg() == false){
		glClearColor(bgColor[0],bgColor[1],bgColor[2], bgColor[3]);
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}
}


//----------------------------------------------------------
void ofSetColor(int _r, int _g, int _b){
	float r = (float)_r / 255.0f; r = MAX(0,MIN(r,1.0f));
	float g = (float)_g / 255.0f; g = MAX(0,MIN(g,1.0f));
	float b = (float)_b / 255.0f; b = MAX(0,MIN(b,1.0f));
	
	glColor4f(r,g,b,1.0f);
}


//----------------------------------------------------------
void ofSetColor(int _r, int _g, int _b, int _a){
	float r = (float)_r / 255.0f; r = MAX(0,MIN(r,1.0f));
	float g = (float)_g / 255.0f; g = MAX(0,MIN(g,1.0f));
	float b = (float)_b / 255.0f; b = MAX(0,MIN(b,1.0f));
	float a = (float)_a / 255.0f; a = MAX(0,MIN(a,1.0f));
	
	glColor4f(r,g,b,a);
}



//----------------------------------------------------------
void ofSetColorHSB(int _h, int _s, int _b){
	float h = (float)_h / 255.0f; h = MAX(0,MIN(h,1.0f));
	float s = (float)_s / 255.0f; s = MAX(0,MIN(s,1.0f));
	float b = (float)_b / 255.0f; b = MAX(0,MIN(b,1.0f));
	
	if(s==0){
		glColor4f(b,b,b,1.0f); return;
	}
	
	int i = floor(h*6);
	float f = h - i;
	if( !(i&1) ) f = 1-f; // if i is even
	float m = b*(1-s);
	float n = b*(1-s*f);
	
	switch(i){
		case 6:
		case 0: glColor4f(b,n,m,1.0f); return;
		case 1: glColor4f(n,b,m,1.0f); return;
		case 2: glColor4f(m,b,n,1.0f); return;
		case 3: glColor4f(m,n,b,1.0f); return;
		case 4: glColor4f(n,m,b,1.0f); return;
		case 5: glColor4f(b,m,n,1.0f); return;
	}
}

//----------------------------------------------------------
void ofSetColorHSB(int _h, int _s, int _b, int _a){
	float h = (float)_h / 255.0f; h = MAX(0,MIN(h,1.0f));
	float s = (float)_s / 255.0f; s = MAX(0,MIN(s,1.0f));
	float b = (float)_b / 255.0f; b = MAX(0,MIN(b,1.0f));
	float a = (float)_a / 255.0f; a = MAX(0,MIN(a,1.0f));
	
	if(s==0){
		glColor4f(b,b,b,a); return;
	}
	
	int i = floor(h*6);
	float f = h - i;
	if( !(i&1) ) f = 1-f; // if i is even
	float m = b*(1-s);
	float n = b*(1-s*f);
	
	switch(i){
		case 6:
		case 0: glColor4f(b,n,m,a); return;
		case 1: glColor4f(n,b,m,a); return;
		case 2: glColor4f(m,b,n,a); return;
		case 3: glColor4f(m,n,b,a); return;
		case 4: glColor4f(n,m,b,a); return;
		case 5: glColor4f(b,m,n,a); return;
	}
}


//----------------------------------------------------------
void ofSetColor(int hexColor){
	int r = (hexColor >> 16) & 0xff;
	int g = (hexColor >> 8) & 0xff;
	int b = (hexColor >> 0) & 0xff;
	ofSetColor(r,g,b);
}

//----------------------------------------------------------
void ofNoFill(){
	drawMode = OF_OUTLINE;
}

//----------------------------------------------------------
void ofFill(){
	drawMode = OF_FILLED;
}

//----------------------------------------------------------
void  ofSetRectMode(int mode){
	if (mode == OF_RECTMODE_CORNER) 		cornerMode = OF_RECTMODE_CORNER;
	else if (mode == OF_RECTMODE_CENTER) 	cornerMode = OF_RECTMODE_CENTER;
}

//----------------------------------------------------------
int ofGetRectMode(){
	return 	cornerMode;
}

//----------------------------------------------------------
void ofEnableAlphaBlending(){
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

//----------------------------------------------------------
void ofDisableAlphaBlending(){
	glDisable(GL_BLEND);
}

//----------------------------------------------------------
void ofTriangle(float x1,float y1,float x2,float y2,float x3, float y3)
{	
	basicShapeCoords[0] = x1;
	basicShapeCoords[1] = y1;
	basicShapeCoords[2] = x2;
	basicShapeCoords[3] = y2;
	basicShapeCoords[4] = x3;
	basicShapeCoords[5] = y3;
	
	glVertexPointer(2, GL_FLOAT, 0, basicShapeCoords);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays( (drawMode==OF_FILLED)?GL_TRIANGLES:GL_LINE_LOOP, 0, 3);
}

//----------------------------------------------------------
void ofRect(float x,float y,float w,float h)
{
	if( drawMode == OF_FILLED ){
		if (cornerMode == OF_RECTMODE_CENTER){
			basicShapeCoords[0] = x-w/2;
			basicShapeCoords[1] = y-h/2;
			basicShapeCoords[2] = x+w/2;
			basicShapeCoords[3] = y-h/2;
			basicShapeCoords[4] = x-w/2;
			basicShapeCoords[5] = y+h/2;
			basicShapeCoords[6] = x+w/2;
			basicShapeCoords[7] = y+h/2;
		} else {
			basicShapeCoords[0] = x;
			basicShapeCoords[1] = y;
			basicShapeCoords[2] = x+w;
			basicShapeCoords[3] = y;
			basicShapeCoords[4] = x;
			basicShapeCoords[5] = y+h;
			basicShapeCoords[6] = x+w;
			basicShapeCoords[7] = y+h;
		}
	}else{
		if (cornerMode == OF_RECTMODE_CENTER){
			basicShapeCoords[0] = x-w/2;
			basicShapeCoords[1] = y-h/2;
			basicShapeCoords[2] = x+w/2;
			basicShapeCoords[3] = y-h/2;
			basicShapeCoords[4] = x+w/2;
			basicShapeCoords[5] = y+h/2;
			basicShapeCoords[6] = x-w/2;
			basicShapeCoords[7] = y+h/2;
		} else {
			basicShapeCoords[0] = x;
			basicShapeCoords[1] = y;
			basicShapeCoords[2] = x+w;
			basicShapeCoords[3] = y;
			basicShapeCoords[4] = x+w;
			basicShapeCoords[5] = y+h;
			basicShapeCoords[6] = x;
			basicShapeCoords[7] = y+h;
		}
	}
	
	glVertexPointer(2, GL_FLOAT, 0, basicShapeCoords);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays( (drawMode==OF_FILLED)?GL_TRIANGLE_STRIP:GL_LINE_LOOP, 0, 4);
}

//----------------------------------------------------------
void ofLine(float x1,float y1, float x2,float y2)
{
	basicShapeCoords[0] = x1;
	basicShapeCoords[1] = y1;
	basicShapeCoords[2] = x2;
	basicShapeCoords[3] = y2;
	
	glVertexPointer(2, GL_FLOAT, 0, basicShapeCoords);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays( GL_LINES, 0, 2);
}

//----------------------------------------------------------
void ofCircle(float x,float y, float radius)
{	
	if (!bSetupCircle) ofSetCircleResolution(circleResolution);

	glPushMatrix();
	glTranslatef(x, y, 0);
	glScalef(radius, radius, 1);	
	glVertexPointer(2, GL_FLOAT, 0, circleCoords);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays( (drawMode==OF_FILLED)?GL_TRIANGLE_FAN:GL_LINE_LOOP, (drawMode==OF_FILLED?0:1), circleResolution + (drawMode==OF_FILLED?2:1));
	glPopMatrix();
}

//----------------------------------------------------------
void ofEllipse(float x, float y, float width, float height)
{
	if (!bSetupCircle) ofSetCircleResolution(circleResolution);
	
	glPushMatrix();
	glTranslatef(x, y, 0);
	glScalef(width, height, 1);
	glVertexPointer(2, GL_FLOAT, 0, circleCoords);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays( (drawMode==OF_FILLED)?GL_TRIANGLE_FAN:GL_LINE_LOOP, (drawMode==OF_FILLED?0:1), circleResolution + (drawMode==OF_FILLED?2:1));
	glPopMatrix();
}

//----------------------------------------------------------
void ofSetCircleResolution(int res){
	if (res > 1){
		res = CLAMP(res, 2, MAX_CIRC_RESOLUTION);
		circleResolution = res;
		circleCoords[0] = 0.0f;
		circleCoords[1] = 0.0f;
		float angle = 0.0f;
		float angleAdder = M_TWO_PI / (float)res;
		for (int i = 0; i < res; i++){
			circleCoords[i*2+2] = cos(angle);
			circleCoords[i*2+3] = sin(angle);
			angle += angleAdder;
		}
		circleCoords[res*2+2] = circleCoords[2];
		circleCoords[res*2+3] = circleCoords[3];
		bSetupCircle = true;
	}
}

//----------------------------------------------------------
void ofBeginShape(){
	vertexCount = 0;
}

//----------------------------------------------------------
void ofVertex(float x, float y){
	vertexCoords[vertexCount<<1] = x;
	vertexCoords[(vertexCount<<1)|1] = y;
	vertexCount++;
}

//----------------------------------------------------------
void ofEndShape(GLint glDrawMode){
	if(glDrawMode==-1)
		glDrawMode = (drawMode==OF_FILLED)?GL_TRIANGLE_FAN:GL_LINE_STRIP;
	glVertexPointer(2, GL_FLOAT, 0, vertexCoords);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(glDrawMode, 0, vertexCount);
}



//----------------------------------------------------------
void ofCurve(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
{	
	float t,t2,t3;
	float x,y;
	
	ofBeginShape();
	
	int resolution = 20;
	for (int i = 0; i < resolution; i++){
		
		t 	=  (float)i / (float)(resolution-1);
		t2 	= t * t;
		t3 	= t2 * t;
		
		x = 0.5f * ( ( 2.0f * x1 ) +
					( -x0 + x2 ) * t +
					( 2.0f * x0 - 5.0f * x1 + 4 * x2 - x3 ) * t2 +
					( -x0 + 3.0f * x1 - 3.0f * x2 + x3 ) * t3 );
		
		y = 0.5f * ( ( 2.0f * y1 ) +
					( -y0 + y2 ) * t +
					( 2.0f * y0 - 5.0f * y1 + 4 * y2 - y3 ) * t2 +
					( -y0 + 3.0f * y1 - 3.0f * y2 + y3 ) * t3 );
		
		ofVertex(x,y);
	}
	
	ofEndShape();
}


//----------------------------------------------------------
void ofBezier(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
{
	float   ax, bx, cx;
    float   ay, by, cy;
    float   t, t2, t3;
    float   x, y;
	
    // polynomial coefficients
    cx = 3.0f * (x1 - x0);
    bx = 3.0f * (x2 - x1) - cx;
    ax = x3 - x0 - cx - bx;
	
    cy = 3.0f * (y1 - y0);
    by = 3.0f * (y2 - y1) - cy;
    ay = y3 - y0 - cy - by;
	
    ofBeginShape();
	
	int resolution = 20;
    for (int i = 0; i < resolution; i++){
    	t 	=  (float)i / (float)(resolution-1);
    	t2 = t * t;
    	t3 = t2 * t;
		x = (ax * t3) + (bx * t2) + (cx * t) + x0;
    	y = (ay * t3) + (by * t2) + (cy * t) + y0;
    	ofVertex(x,y);
    }
	
    ofEndShape();
}
